package com.hspedu.bot.consume.utils;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hspedu.bot.consume.WebSocketServer;
import com.hspedu.bot.pojo.Bot;
import com.hspedu.bot.pojo.Record;
import com.hspedu.bot.pojo.User;
import com.hspedu.bot.utils.Cell;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;

public class Game extends Thread {
    final private Integer rows;
    final private Integer cols;
    final private Integer inner_walls_count;
    final private int[][] g;
    final private static int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
    private Player playerA, playerB;  //player表示左下角和右上角的玩家

    //两名玩家的下一步操作(线程间通信要加锁),0123表示上右下左（与前端一致）
    private Integer nextStepA = null;
    private Integer nextStepB = null;

    private ReentrantLock lock = new ReentrantLock();       //线程锁
    private String status = "playing";                      //当前玩家状态[playing、over]
    private String loser = "";                              //败者[all平局、A输、B输]
    private final static String addBotUrl = "http://127.0.0.1:3002/bot/add/";

    public void setNextStepA(Integer nextStepA) {
        lock.lock();
        try {
            this.nextStepA = nextStepA;
        } finally {
            lock.unlock();
        }
    }

    public void setNextStetB(Integer nextStepB) {
        lock.lock();
        try {
            this.nextStepB = nextStepB;
        } finally {
            lock.unlock();
        }
    }

    //获得玩家信息
    public Player getPlayerA() {
        return playerA;
    }

    public Player getPlayerB() {
        return playerB;
    }

    /***
     * 将player的地图信息转换成字符串在发送给bot端
     * 地图#我的横坐标#我的纵坐标#(我的操作)#对手的横坐标#对手的纵坐标#对手的操作
     **/
    private String getInput(Player player) {  // 将当前的局面信息，编码成字符串
        Player me, you;
        if (playerA.getId().equals(player.getId())) {
            me = playerA;
            you = playerB;
        } else {
            me = playerB;
            you = playerA;
        }

        return getMapString() + "#" +
                me.getSx() + "#" +
                me.getSy() + "#(" +
                me.getStepString() + ")#" +
                you.getSx() + "#" +
                you.getSy() + "#(" +
                you.getStepString() + ")";
    }

    //发送给BotRunningController的bot信息
    private void sendBotCode(Player player) {
        if (player.getBotId() == -1) return;
        MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
        data.add("user_id", player.getId().toString());
        data.add("bot_code", player.getBotCode());
        data.add("bot_input", getInput(player));
        System.out.println(data);
        WebSocketServer.restTemplate.postForObject(addBotUrl, data, String.class);
    }


    //等待玩家的下一步操作
    private boolean nextStep() {
        try {
            Thread.sleep(200);          //前端每秒走5格子
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //向bot微服务发送链接
        sendBotCode(playerA);
        sendBotCode(playerB);

        //总共等待5秒的时间
        for (int i = 0; i < 50; i++) {
            try {
                Thread.sleep(100);
                lock.lock();
                try {
                    if (nextStepA != null && nextStepB != null) {
                        playerA.getSteps().add(nextStepA);
                        playerB.getSteps().add(nextStepB);
                        return true;
                    }
                } finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    //判断蛇头是否碰到蛇身(自己的蛇身或者对方的蛇身都要判断)、或者碰到墙
    //合法返回true
    private boolean check_valid(List<Cell> cellsA, List<Cell> cellsB) {
        int n = cellsA.size();
        Cell cell = cellsA.get(n - 1);  //取出最后一位(蛇头)
        if (g[cell.x][cell.y] == 1)      //如果最后一位是墙
            return false;
        for (int i = 0; i < n - 1; i++) {
            if (cellsA.get(i).x == cell.x && cellsA.get(i).y == cell.y)
                return false;
        }
        for (int i = 0; i < n - 1; i++) {
            if (cellsB.get(i).x == cell.x && cellsB.get(i).y == cell.y)
                return false;
        }
        return true;
    }

    //判断两名玩家的下一步是否合法
    private boolean judge() {
        List<Cell> cellsA = playerA.getCells();
        List<Cell> cellsB = playerB.getCells();
        boolean validA = check_valid(cellsA, cellsB);
        boolean validB = check_valid(cellsB, cellsA);
        if (!validA || !validB)      //只要发现有不合法的情况
        {
            this.status = "over";
            if (!validA && !validB)
                this.loser = "all";
            else if (!validA)
                this.loser = "A";
            else
                this.loser = "B";
        }

        return false;
    }


    //向每一个人发送信息(后端向前端)
    private void sendAllMessage(String message) {
        if (WebSocketServer.users.get(playerA.getId()) != null)
            WebSocketServer.users.get(playerA.getId()).sendMessage(message);
        if (WebSocketServer.users.get(playerB.getId()) != null)
            WebSocketServer.users.get(playerB.getId()).sendMessage(message);
    }

    //向两个client传递移动信息(其实就是双发的移动传送到后端,绘制新的地图发送到前端)
    private void sendMove() {
        lock.lock();
        try {
            JSONObject resp = new JSONObject();
            resp.put("event", "move");
            resp.put("a_direction", nextStepA);
            resp.put("b_direction", nextStepB);
            sendAllMessage(resp.toJSONString());
            nextStepA = nextStepB = null;
        } finally {
            lock.unlock();
        }
    }

    //向两个Client返回游戏结果
    private void sendResult() {
        JSONObject resp = new JSONObject();
        resp.put("event", "result");
        resp.put("loser", loser);
        saveToDataBase();
        sendAllMessage(resp.toJSONString());
    }


    //构造方法
    public Game(Integer rows, Integer cols, Integer inner_walls_count, Integer idA, Bot botA, Integer idB, Bot botB) {
        this.rows = rows;
        this.cols = cols;
        this.inner_walls_count = inner_walls_count;
        this.g = new int[rows][cols];

        //取出bot信息
        Integer botIdA = -1, botIdB = -1;
        String botCodeA = "", botCodeB = "";
        if (botA != null)                //如果是亲自出马,则没有bot信息
        {
            botIdA = botA.getId();
            botCodeA = botA.getContent();
        }
        if (botB != null) {
            botIdB = botB.getId();
            botCodeB = botB.getContent();
        }

        playerA = new Player(idA, botIdA, botCodeA, this.rows - 2, 1, new ArrayList<>());
        playerB = new Player(idB, botIdB, botCodeB, 1, this.cols - 2, new ArrayList<>());
    }

    public int[][] getG() {
        return g;
    }

    private boolean check_connectivity(int sx, int sy, int tx, int ty) {
        if (sx == tx && sy == ty) return true;
        g[sx][sy] = 1;

        for (int i = 0; i < 4; i++) {
            int x = sx + dx[i], y = sy + dy[i];
            if (x >= 0 && x < this.rows && y >= 0 && y < this.cols && g[x][y] == 0) {
                if (check_connectivity(x, y, tx, ty)) {
                    g[sx][sy] = 0;
                    return true;
                }
            }
        }

        g[sx][sy] = 0;
        return false;
    }

    private boolean draw() {  // 画地图
        for (int i = 0; i < this.rows; i++) {
            for (int j = 0; j < this.cols; j++) {
                g[i][j] = 0;
            }
        }

        for (int r = 0; r < this.rows; r++) {
            g[r][0] = g[r][this.cols - 1] = 1;
        }
        for (int c = 0; c < this.cols; c++) {
            g[0][c] = g[this.rows - 1][c] = 1;
        }

        Random random = new Random();
        for (int i = 0; i < this.inner_walls_count / 2; i++) {
            for (int j = 0; j < 1000; j++) {
                int r = random.nextInt(this.rows);
                int c = random.nextInt(this.cols);

                if (g[r][c] == 1 || g[this.rows - 1 - r][this.cols - 1 - c] == 1)
                    continue;
                if (r == this.rows - 2 && c == 1 || r == 1 && c == this.cols - 2)
                    continue;

                g[r][c] = g[this.rows - 1 - r][this.cols - 1 - c] = 1;
                break;
            }
        }

        return check_connectivity(this.rows - 2, 1, 1, this.cols - 2);
    }

    public void createMap() {
        for (int i = 0; i < 1000; i++) {
            if (draw())
                break;
        }
    }

    private String getMapString() {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < this.rows; i++) {
            for (int j = 0; j < this.cols; j++)
                res.append(g[i][j]);
        }
        return res.toString();
    }

    /**
     * 根据战局结果更新User分数
     */
    private void updateUserRating(Player player, Integer rating) {
        User user = WebSocketServer.userService.getById(player.getId());
        user.setRating(rating);
        WebSocketServer.userService.updateById(user);
    }



    //将战局结果保存到数据库record表
    private void saveToDataBase() {

        //查到用户得分
        Integer ratingA = WebSocketServer.userService.getById(playerA.getId()).getRating();
        Integer ratingB = WebSocketServer.userService.getById(playerB.getId()).getRating();
        if ("A".equals(loser)) {
            ratingB += 5;
            ratingA -= 2;
        } else {
            ratingA += 5;
            ratingB -= 2;
        }
        updateUserRating(playerA, ratingA);
        updateUserRating(playerB, ratingB);

        Record record = new Record(
                null,
                playerA.getId(),
                playerA.getSx(),
                playerA.getSy(),
                playerB.getId(),
                playerB.getSx(),
                playerB.getSy(),
                playerA.getStepString(),
                playerB.getStepString(),
                getMapString(),
                loser,
                new Date()
        );
        WebSocketServer.recordService.save(record);
    }

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) { // 最多循环1000步
            if (nextStep()) {            //捕获两条蛇是否移动
                judge();                 //判断输赢

                if ("playing".equals(status)) {
                    sendMove();         //将对手玩家的输入广播给Client
                } else {
                    sendResult();
                    break;
                }
            } else {                    //超时(没有输入的一方判输)
                status = "over";

                lock.lock();
                try {
                    if (nextStepA == null && nextStepB == null) {
                        loser = "all";
                    } else if (nextStepA == null) {
                        loser = "A";
                    } else {    //nextStep() = false会有卡超时边界依然输入的情况，但是为了规则合理性，在这里全部判输了
                        loser = "B";
                    }
                } finally {
                    lock.unlock();
                }
                sendResult();
                break;
            }
        }
    }
}
